<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="utf-8">
	<meta http-equiv="x-ua-compatible" content="ie=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
	<title>裁剪</title>
	<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.2/css/bootstrap.min.css">
	<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.4.3/cropper.min.css">
	<style>
		.container {
			display: none;
		}

		/* gallery */
		.gallery {
			overflow-x: auto;
			height: 220px;
			display: flex;
		}

		.gallery div {
			flex: auto;
			width: fit-content;
			margin: 0 10px;
			position: relative;
		}

		.gallery div img {
			height: 100%;
			width: auto;
		}

		.gallery div.active::after {
			content: " ";
			position: absolute;
			left: 0;
			top: 0;
			display: block;
			width: 100%;
			height: 100%;
			background-color: rgba(0, 0, 0, 0.5);
		}

		/* progress */
		.progress {
			display: none;
			margin-bottom: 1rem;
		}

		.alert {
			display: none;
		}

		.row {
			margin-top: 30px;
		}

		/* featured */
		#crop-pc .image,
		#crop-mobile .image {
			margin: 0 auto;
			/* max-height: 497px; */
			position: relative;
		}

		.image img.origin {
			max-width: 100%;
			max-height: 100%;
		}

		#crop-pc .image {
			width: 300px;
			height: 400px;
		}

		#crop-mobile .image {
			width: 240px;
			height: 200px;
		}

		/* mask prompt */
		.mask.prompt {
			position: absolute;
			left: 0;
			bottom: 0;
			width: 100%;
			height: 30px;
			line-height: 30px;
			background-color: rgba(0, 0, 0, 0.5);
			text-align: center;
			overflow: hidden;
		}

		.mask.prompt.horizontal:after {
			display: block;
			background-image: url("./images/horizontal.png");
			background-size: auto 16px;
			background-repeat: no-repeat;
			background-position: left;
			content: '左右调整图片，选择合适区域生成缩略图';
			font-size: 14px;
			color: #fff;
			padding-left: 40px;
		}

		.mask.prompt.vertical:after {
			display: block;
			background-image: url("./images/vertical.png");
			background-size: auto 26px;
			background-repeat: no-repeat;
			background-position: 0 2px;
			content: '上下调整图片，选择合适区域生成缩略图';
			font-size: 14px;
			color: #fff;
			padding-left: 25px;
		}

		.mask.prompt.exact:after {
			background-image: url("./images/exact.png");
			background-size: auto 16px;
			background-repeat: no-repeat;
			background-position: left;
			content: '完美的缩略图';
			font-size: 14px;
			color: #fff;
			padding-left: 30px;
		}
	</style>
</head>

<body>
	<div class="container">

		<h1>上传文章feature image并裁剪生成缩略图</h1>

		<div class="row">
			<div class="col-md-9 gallery">
			</div>
			<div class="col-md-3">
				<button class="btn btn-success" id="next">下一张</button>
			</div>
		</div>

		<div class="progress">
			<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="0"
				aria-valuemin="0" aria-valuemax="100">0%
			</div>
		</div>
		<div class="alert" role="alert"></div>

		<div class="row" id="crop-pc">
			<div class="col-md-9">
				<div class="image">
					<img src="" alt="" class="origin">

					<div class="mask prompt"></div>
				</div>
			</div>
			<div class="col-md-3">
				<button class="btn btn-success btn-crop">裁剪</button>
				<div>
					<img class="preview" src="" alt="">
				</div>
			</div>
		</div>

		<div class="row" id="crop-mobile">
			<div class="col-md-9">
				<div class="image">
					<img src="" alt="" class="origin">

					<div class="mask prompt"></div>
				</div>
			</div>
			<div class="col-md-3">
				<button class="btn btn-success btn-crop">裁剪</button>
				<div>
					<img class="preview" src="" alt="">
				</div>
			</div>
		</div>
	</div>
	<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
	<script src="//cdn.jsdelivr.net/npm/jquery.scrollto@2.1.2/jquery.scrollTo.min.js"></script>
	<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.2/js/bootstrap.bundle.min.js"></script>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.4.3/cropper.min.js"></script>

	<script>
		$(function () {
			// let images = []
			let images = [
				'./images/x.png',
				'./images/y.png',
				'./images/z.png',
				'./images/ragnarok.jpg',
				'./images/moon_flag_aldrin_apollo11_600x500.jpg'
			]
			if (images.length === 0) {
				return
			} else {
				$('.container').css({ 'display': 'block' })
			}

			// image index
			let imageIndex = 0
			const nextImage = () => {
				imageIndex += 1
				if (imageIndex === images.length) {
					imageIndex = 0
				}
				return imageIndex
			}

			// event
			const createCropperEvent = new CustomEvent('create-cropper', {
				bubbles: true,
				detail: {
					text: () => "it's time to create a new cropper instance"
				}
			})
			const destroyCropperEvent = new CustomEvent('destroy-cropper', {
				bubbles: true,
				detail: {
					text: () => "it's time to destroy the cropper instance"
				}
			})

			// for demonstration
			const gallery = document.querySelector('.gallery')
			const clickNextSetImageMask = function () {
				gallery.querySelectorAll('div').forEach((div, index) => {
					div.classList.remove('active')
					if (imageIndex === index) {
						div.classList.add('active')
						// scrollTo jquery plugin
						$(gallery).scrollTo($(div))
					}
					// console.log(div, index)
				})
			}
			images.forEach((image, index) => {
				const div = document.createElement('div')
				const img = document.createElement('img')
				img.src = image
				div.appendChild(img)
				gallery.appendChild(div)
				img.addEventListener('click', function (e) {
					gallery.querySelectorAll('div.active').forEach(div => {
						div.classList.remove('active')
					})
					e.target.parentNode.classList.add('active')
					// console.log(e.target.src)
					imageIndex = index
					// e.target.dispatchEvent(destroyCropperEvent)
					dispatch()
				})
			})

			// featured
			var $progress = $('.progress');
			var $progressBar = $('.progress-bar');
			var $alert = $('.alert');

			let configs = [
				{
					id: 'crop-pc',
					width: 150,
					height: 200
				},
				{
					id: 'crop-mobile',
					width: 120,
					height: 100
				}
			]

			// next
			const next = () => {
				nextImage()

				clickNextSetImageMask()
				// console.log(imageIndex)
				// e.target.dispatchEvent(destroyCropperEvent)
				dispatch()
			}
			const dispatch = () => {
				configs.forEach(config => {
					// 从内部触发事件
					document.getElementById(config.id).querySelector('button').dispatchEvent(destroyCropperEvent)
				})
			}
			document.querySelector('#next').addEventListener('click', next)


			configs.forEach(config => {

				let container = document.getElementById(config.id)
				let image = container.querySelector('img.origin')
				let preview = container.querySelector('img.preview')
				let maskPrompt = container.querySelector('.mask.prompt')

				let cropper
				// create
				container.addEventListener('create-cropper', e => {
					//
					if (images[imageIndex] === undefined) {
						console.error('error occurs when get image')
						return
					}
					image.src = images[imageIndex]

					// console.log(imageIndex)
					// console.log(e.detail.text())

					cropper = new Cropper(image, {
						aspectRatio: config.width / config.height,
						viewMode: 3,
						dragMode: 'move',
						center: false,
						// guides: false,
						highlight: false,
						cropBoxMovable: false,
						cropBoxResizable: false,
						toggleDragModeOnDblclick: false,
						rotatable: false,
						scalable: false,
						zoomable: false,
						zoomOnTouch: false,
						zoomOnWheel: false,
						ready: function () {
							var cropper = this.cropper
							var containerData = cropper.getContainerData();
							cropper.setCropBoxData({
								width: containerData.width,
								height: containerData.height
							})
							// mask prompt
							let imageData = cropper.getImageData()
							// 150/200
							const ratio = 100 * config.width / config.height
							maskPrompt.classList.remove('exact')
							maskPrompt.classList.remove('horizontal')
							maskPrompt.classList.remove('vertical')
							if (imageData.aspectRatio * 100 == ratio) {
								maskPrompt.classList.add('exact')
							} else if (imageData.aspectRatio * 100 > ratio) {
								maskPrompt.classList.add('horizontal')
							} else if (imageData.aspectRatio * 100 < ratio) {
								maskPrompt.classList.add('vertical')
							}
						}
					});
				})
				// destroy
				container.addEventListener('destroy-cropper', e => {
					// console.log(e.detail.text())
					// console.log(e)
					if (cropper) cropper.destroy()
					cropper = null
					e.target.dispatchEvent(createCropperEvent)
				})

				// document.getElementById('feature-crop-it').addEventListener('click', function () {
				container.querySelector('.btn-crop').addEventListener('click', function () {
					var initialAvatarURL;
					var canvas;

					if (cropper) {
						canvas = cropper.getCroppedCanvas({
							width: config.width,
							height: config.height,
							// fillColor: 'white'
						});
						initialAvatarURL = preview.src;
						preview.src = canvas.toDataURL();
						$progress.show();
						$alert.removeClass('alert-success alert-warning');
						canvas.toBlob(function (blob) {
							var formData = new FormData();

							formData.append('avatar', blob, 'avatar.jpg');

							// only for test
							// replace with your own server request
							$.ajax('https://jsonplaceholder.typicode.com/posts', {
								method: 'POST',
								data: formData,
								processData: false,
								contentType: false,

								xhr: function () {
									var xhr = new XMLHttpRequest();

									xhr.upload.onprogress = function (e) {
										var percent = '0';
										var percentage = '0%';

										if (e.lengthComputable) {
											percent = Math.round((e.loaded / e.total) * 100);
											percentage = percent + '%';
											$progressBar.width(percentage).attr('aria-valuenow', percent).text(percentage);
										}
									};

									return xhr;
								},

								success: function () {
									$alert.show().addClass('alert-success').text('Upload success');
								},

								error: function () {
									preview.src = initialAvatarURL;
									$alert.show().addClass('alert-warning').text('Upload error');
								},

								complete: function () {
									$progress.hide();
								},
							});
						});
					}
				});

			})

			// init
			const init = () => {
				configs.forEach(config => {
					document.getElementById(config.id).querySelector('button').dispatchEvent(createCropperEvent)
				})
			}
			init()
		})
	</script>
</body>

</html>